//
//  Level.m
//  CandyCrushClone
//
//  Created by  Stefan Nestorov on 12/29/14.
//  Copyright (c) 2014 Nestorov Engineering. All rights reserved.
//

#import "RWTLevel.h"
#import "CommunicationManager.h"
#import "GameInfo.h"
#import "Globals.h"

@interface RWTLevel ()

@property (strong, nonatomic) NSSet *possibleSwaps;
@property (assign, nonatomic, readwrite) BOOL isSimulation;

@end

@implementation RWTLevel {
    
    RWTCookie *_cookies[NumColumns][NumRows];
    RWTTile *_tiles[NumColumns][NumRows];
    LevelAdapter *_levelAdapter;
    NSSet *_initialServerCookies;
}


- (instancetype)initWithFile:(NSString *)filename
{
    
    self = [super init];
    
    if (self != nil) {
        NSDictionary *dictionary = [self loadJSON:filename];
        
        // Loop through the rows
        [dictionary[@"tiles"] enumerateObjectsUsingBlock:^(NSArray *array, NSUInteger row, BOOL *stop) {
            
            // Loop through the columns in the current row
            [array enumerateObjectsUsingBlock:^(NSNumber *value, NSUInteger column, BOOL *stop) {
                
                // Note: In Sprite Kit (0,0) is at the bottom of the screen,
                // so we need to read this file upside down.
                NSInteger tileRow = NumRows - row - 1;
                
                // If the value is 1, create a tile object.
                if ([value integerValue] == 1) {
                    _tiles[column][tileRow] = [[RWTTile alloc] init];
                }
            }];
        }];
        
        self.targetScore = [dictionary[@"targetScore"] unsignedIntegerValue];
        self.maximumMoves = [dictionary[@"moves"] unsignedIntegerValue];
        
        _levelAdapter = [LevelAdapter sharedAdapter];
        
    }
    
    return self;
    
}

- (NSDictionary *)loadJSON:(NSString *)filename
{
    
    NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:@"json"];
    if (path == nil) {
        NSLog(@"Could not find level file: %@", filename);
        return nil;
    }
    
    NSError *error;
    NSData *data = [NSData dataWithContentsOfFile:path options:0 error:&error];
    if (data == nil) {
        NSLog(@"Could not load level file: %@, error: %@", filename, error);
        return nil;
    }
    
    NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
    if (dictionary == nil || ![dictionary isKindOfClass:[NSDictionary class]]) {
        NSLog(@"Level file '%@' is not valid JSON: %@", filename, error);
        return nil;
    }
    
    return dictionary;
    
}

- (RWTTile *)tileAtColumn:(NSInteger)column row:(NSInteger)row
{
    
    NSAssert1(column >= 0 && column < NumColumns, @"Invalid column: %ld", (long)column);
    NSAssert1(row >= 0 && row < NumRows, @"Invalid row: %ld", (long)row);
    
    return _tiles[column][row];
    
}

- (RWTCookie *)cookieAtColumn:(NSInteger)column row:(NSInteger)row
{
    
    NSAssert1(column >= 0 && column < NumColumns, @"Invalid column: %ld", (long)column);
    NSAssert1(row >= 0 && row < NumRows, @"Invalid row: %ld", (long)row);
    
    return _cookies[column][row];
    
}

#pragma mark - Create Initial cookies
// NOT USED
- (NSSet *)createInitialCookies
{
    
    NSMutableSet *set = [NSMutableSet set];
    
    for (NSInteger row = 0; row < NumRows; row++) {
        for (NSInteger column = 0; column < NumColumns; column++) {
            
            if (_tiles[column][row] != nil) {
                
                NSUInteger cookieType;
                do {
                    cookieType = arc4random_uniform(NumCookieTypes) + 1;
                }
                while ((column >= 2 &&
                        _cookies[column - 1][row].cookieType == cookieType &&
                        _cookies[column - 2][row].cookieType == cookieType)
                       ||
                       (row >= 2 &&
                        _cookies[column][row - 1].cookieType == cookieType &&
                        _cookies[column][row - 2].cookieType == cookieType));
                
                RWTCookie *cookie = [self createCookieAtColumn:column row:row withType:cookieType];
                [set addObject:cookie];
            }
            
        }
    }
    
    return set;
    
}


- (RWTCookie *)createCookieAtColumn:(NSInteger)column row:(NSInteger)row withType:(NSUInteger)cookieType
{
    
    RWTCookie *cookie = [[RWTCookie alloc] init];
    cookie.cookieType = cookieType;
    cookie.column = column;
    cookie.row = row;
    _cookies[column][row] = cookie;
    
    return cookie;
    
}

- (NSSet *)shuffle
{
 
    // There is Shuffle on Server so update Level
    if ( [GameInfo sharedInfo].shuffleCookiesSet != nil ) {
        
        NSSet *shuffleCookiesFromServer = [[GameInfo sharedInfo].shuffleCookiesSet copy];
        
        // remove server cookies in GameInfo
        [GameInfo sharedInfo].shuffleCookiesSet = nil;
        
        [self populateCookies:shuffleCookiesFromServer];
        [self detectPossibleSwaps];
        
        return shuffleCookiesFromServer;
        
    }
    
    // get Initial cookies
    return _initialServerCookies;
    
}


- (NSSet *)getCurrentBoardFromServerIfNecessary
{
    
    NSMutableSet *currentBoard = [NSMutableSet new];
    
    // there is NO currentBoardCookiesSet
    if ( [GameInfo sharedInfo].currentBoardCookiesSet == nil ) {
        return currentBoard;
    }
    
    BOOL shouldUpdateBoard = NO;
    
    NSSet *currentBoardFromServer = [GameInfo sharedInfo].currentBoardCookiesSet;
    
    for ( RWTCookie *cookie in currentBoardFromServer ) {
        
        if ( _cookies[cookie.column][cookie.row].cookieType != cookie.cookieType ) {
            
            // set new Cookie
            _cookies[cookie.column][cookie.row] = cookie;
            
            shouldUpdateBoard = YES;
            
        }
        
    }
    
    // Detect Swaps if there is change on the Board
    if ( shouldUpdateBoard == YES ) {
        
        NSLog(@"shouldUpdateBoard == YES");
        [self detectPossibleSwaps];
        
        // get current board
        for (NSInteger row = 0; row < NumRows; row++) {
            for (NSInteger column = 0; column < NumColumns; column++) {
                
                if ( _cookies[column][row] != nil ) {
                    [currentBoard addObject:_cookies[column][row]];
                }
                
            }
        }
        
    }
    
    return [NSSet setWithSet:currentBoard];
    
}


#pragma mark - Detect possible swaps

- (void)detectPossibleSwaps
{
    
    NSMutableSet *set = [NSMutableSet set];
    
    for (NSInteger row = 0; row < NumRows; row++) {
        for (NSInteger column = 0; column < NumColumns; column++) {
            
            RWTCookie *cookie = _cookies[column][row];
            if (cookie != nil) {
                
                // Is it possible to swap this cookie with the one on the right?
                if (column < NumColumns - 1) {
                    // Have a cookie in this spot? If there is no tile, there is no cookie.
                    RWTCookie *other = _cookies[column + 1][row];
                    if (other != nil) {
                        // Swap them
                        _cookies[column][row] = other;
                        _cookies[column + 1][row] = cookie;
                        
                        // Is either cookie now part of a chain?
                        if ([self hasChainAtColumn:column + 1 row:row] ||
                            [self hasChainAtColumn:column row:row]) {
                            
                            RWTSwap *swap = [[RWTSwap alloc] init];
                            swap.cookieA = cookie;
                            swap.cookieB = other;
                            [set addObject:swap];
                        }
                        
                        // Swap them back
                        _cookies[column][row] = cookie;
                        _cookies[column + 1][row] = other;
                    }
                }
                
                if (row < NumRows - 1) {
                    
                    RWTCookie *other = _cookies[column][row + 1];
                    if (other != nil) {
                        // Swap them
                        _cookies[column][row] = other;
                        _cookies[column][row + 1] = cookie;
                        
                        if ([self hasChainAtColumn:column row:row + 1] ||
                            [self hasChainAtColumn:column row:row]) {
                            
                            RWTSwap *swap = [[RWTSwap alloc] init];
                            swap.cookieA = cookie;
                            swap.cookieB = other;
                            [set addObject:swap];
                        }
                        
                        _cookies[column][row] = cookie;
                        _cookies[column][row + 1] = other;
                    }
                }
                
            }
        }
    }
    
    self.possibleSwaps = set;
    
}

- (BOOL)hasChainAtColumn:(NSInteger)column row:(NSInteger)row
{
    
    NSUInteger cookieType = _cookies[column][row].cookieType;
    
    //Check f there is the same of this cookie to left and right
    NSUInteger horzLength = 1;
    for (NSInteger i = column - 1; i >= 0 && _cookies[i][row].cookieType == cookieType; i--, horzLength++) ;
    for (NSInteger i = column + 1; i < NumColumns && _cookies[i][row].cookieType == cookieType; i++, horzLength++) ;
    
    if (horzLength >= 3) return YES;
    
    //Check f there is the same of this cookie above and below
    NSUInteger vertLength = 1;
    for (NSInteger i = row - 1; i >= 0 && _cookies[column][i].cookieType == cookieType; i--, vertLength++) ;
    for (NSInteger i = row + 1; i < NumRows && _cookies[column][i].cookieType == cookieType; i++, vertLength++) ;
    
    return (vertLength >= 3);
    
}

- (BOOL)isPossibleSwap:(RWTSwap *)swap
{
    
    return [self.possibleSwaps containsObject:swap];
    
}

- (void)performSwap:(RWTSwap *)swap isSimulation:(BOOL)isSimulation
{
    
    NSLog(@"Swap made: %@", swap);
    _isSimulation = isSimulation;
    // Send SWAP to server if isMyTurn
    if ( isSimulation == NO ) {
        
        [GameInfo sharedInfo].isMyTurn = NO;
        [[CommunicationManager sharedManager] sendSwapSFSObjectToServer:[swap asSFSObject]];
        
    }
    
    NSInteger columnA = swap.cookieA.column;
    NSInteger rowA = swap.cookieA.row;
    NSInteger columnB = swap.cookieB.column;
    NSInteger rowB = swap.cookieB.row;
    
    _cookies[columnA][rowA] = swap.cookieB;
    swap.cookieB.column = columnA;
    swap.cookieB.row = rowA;
    
    _cookies[columnB][rowB] = swap.cookieA;
    swap.cookieA.column = columnB;
    swap.cookieA.row = rowB;
        
}

#pragma mark - Scan and remove cookie matches

- (NSSet *)removeMatches
{
    
    NSSet *horizontalChains = [self detectHorizontalMatches];
    NSSet *verticalChains = [self detectVerticalMatches];
    
    [self removeCookies:horizontalChains];
    [self removeCookies:verticalChains];
    [self calculateScores:horizontalChains];
    [self calculateScores:verticalChains];
    
    return [horizontalChains setByAddingObjectsFromSet:verticalChains];
    
}

- (void)removeCookies:(NSSet *)chains
{
    
    for (RWTChain *chain in chains) {
        for (RWTCookie *cookie in chain.cookies) {
            _cookies[cookie.column][cookie.row] = nil;
        }
    }
    
}

- (NSSet *)detectHorizontalMatches
{
    
    NSMutableSet *set = [NSMutableSet set];
    
    for (NSInteger row = 0; row < NumRows; row++) {
        for (NSInteger column = 0; column < NumColumns - 2; ) {
  
            if (_cookies[column][row] != nil) {
                NSUInteger matchType = _cookies[column][row].cookieType;
                
                if (_cookies[column + 1][row].cookieType == matchType
                    && _cookies[column + 2][row].cookieType == matchType) {
                    
                    RWTChain *chain = [[RWTChain alloc] init];
                    chain.chainType = ChainTypeHorizontal;
                    do {
                        [chain addCookie:_cookies[column][row]];
                        column += 1;
                    }
                    while (column < NumColumns && _cookies[column][row].cookieType == matchType);
                    
                    [set addObject:chain];
                    continue;
                }
            }
            
            column += 1;
        }
        
    }
    
    return set;
    
}

- (NSSet *)detectVerticalMatches
{
    
    NSMutableSet *set = [NSMutableSet set];
    
    for (NSInteger column = 0; column < NumColumns; column++) {
        for (NSInteger row = 0; row < NumRows - 2; ) {
            
            if (_cookies[column][row] != nil) {
                NSUInteger matchType = _cookies[column][row].cookieType;
                
                if (_cookies[column][row + 1].cookieType == matchType
                    && _cookies[column][row + 2].cookieType == matchType) {
                    
                    RWTChain *chain = [[RWTChain alloc] init];
                    chain.chainType = ChainTypeVertical;
                    do {
                        [chain addCookie:_cookies[column][row]];
                        row += 1;
                    }
                    while (row < NumRows && _cookies[column][row].cookieType == matchType);
                    
                    [set addObject:chain];
                    continue;
                }
            }
            row += 1;
        }
    }
    
    return set;
    
}

#pragma mark - Fill holes

- (NSArray *)fillHoles
{
    
    NSMutableArray *columns = [NSMutableArray array];
    
    for (NSInteger column = 0; column < NumColumns; column++) {
        
        NSMutableArray *array;
        for (NSInteger row = 0; row < NumRows; row++) {
            
            if (_tiles[column][row] != nil && _cookies[column][row] == nil) {
                
                for (NSInteger lookup = row + 1; lookup < NumRows; lookup++) {
                    RWTCookie *cookie = _cookies[column][lookup];
                    if (cookie != nil) {
                        
                        _cookies[column][lookup] = nil;
                        _cookies[column][row] = cookie;
                        cookie.row = row;
                        
                        if (array == nil) {
                            array = [NSMutableArray array];
                            [columns addObject:array];
                        }
                        [array addObject:cookie];
                        
                        break;
                        
                    }
                }
            }
        }
    }
    
    return columns;
    
}

#pragma mark - Spawn new cookies

- (NSArray *)topUpCookies
{
    
    // get TopUpCookies from server
    NSArray* topUpCookies = [_levelAdapter getTopUpCookies];
    
    for (NSArray *columns in topUpCookies) {
        for (RWTCookie *cookie in columns) {
            
            _cookies[cookie.column][cookie.row] = cookie;
            
        }
    }
    
//    NSLog(@"Server TUC: %@",topUpCookies);
    return topUpCookies;
    
}

- (void)calculateScores:(NSSet *)chains
{
    
    // add Cookies for player who is on turn
    if ( _isSimulation == YES ) {
        
        [[GameInfo sharedInfo].otherPlayer calculateScoreForChains:chains];
        
    } else {
        
        [[GameInfo sharedInfo].localPlayer calculateScoreForChains:chains];
        
    }
    
}


-(void)populateCookies:(NSSet *)cookiesSet
{
    
    for ( RWTCookie *cookie in cookiesSet ) {
        _cookies[cookie.column][cookie.row] = cookie;
    }
    
//    [self printTiles];
//    [self printCookies];
    
}


- (RWTSwap *)getHintPossibleSwap
{
    
    return [_possibleSwaps anyObject];
    
}


#pragma mark - LevelAdapterDelegate methods
- (void)setInitialCookies:(NSSet *)cookies
{

    _initialServerCookies = cookies;
    
    [self populateCookies:_initialServerCookies];
    [self detectPossibleSwaps];
    
    // send notification to the VC to start the game
    [[NSNotificationCenter defaultCenter] postNotificationName:BEGIN_GAME object:nil];
    
}


#pragma mark Helper Methods

-(void)printTiles
{
    
    NSLog(@"Tiles:");
    
    NSMutableString *tileRow = [NSMutableString new];
    
    for (int row = 9 - 1; row >= 0; row--) {
        
        tileRow = [NSMutableString stringWithFormat:@"Row: %d --- ",row];
        
        for (int col = 0; col < 9 ; col++) {
            
            if ( _tiles[col][row] ) {
                [tileRow appendString:[NSString stringWithFormat:@"1"]];
                
            } else {
                [tileRow appendString:[NSString stringWithFormat:@"0"]];
            }
            
        }
        NSLog(@"%@",tileRow);
        tileRow = [NSMutableString new];
    }
    
    NSLog(@" ");

}


-(void)printCookies
{
    
    NSLog(@"Cookies:");
    
    NSMutableString *cookieRow = [NSMutableString new];
    
    for (int row = 9 - 1; row >= 0; row--) {
        
        cookieRow = [NSMutableString stringWithFormat:@"Row: %d --- ",row];
        
        for (int col = 0; col < 9 ; col++) {
            
            if ( _cookies[col][row] ) {
                [cookieRow appendString:[NSString stringWithFormat:@"%lu", (unsigned long)((RWTCookie *)_cookies[col][row]).cookieType]];
                
            } else {
                [cookieRow appendString:[NSString stringWithFormat:@"_"]];
            }
            
        }
        NSLog(@"%@",cookieRow);
        cookieRow = [NSMutableString new];
    }
    
    NSLog(@" ");
    
}

@end












